home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / C++ / DirectSound / StreamData / streamdata.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-09-27  |  16.6 KB  |  482 lines

  1. //----------------------------------------------------------------------------
  2. // File: StreamData.cpp
  3. //
  4. // Desc: The StreamData sample shows how to streaming wave data into 
  5. //       a DirectSound buffer.
  6. //
  7. // Copyright (c) Microsoft Corp. All rights reserved.
  8. //-----------------------------------------------------------------------------
  9. #include "dxstdafx.h"
  10. #include <commdlg.h>
  11. #include "resource.h"
  12.  
  13.  
  14.  
  15.  
  16. //-----------------------------------------------------------------------------
  17. // Function-prototypes
  18. //-----------------------------------------------------------------------------
  19. INT_PTR CALLBACK MainDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
  20. DWORD WINAPI NotificationProc( LPVOID lpParameter );
  21. VOID    OnInitDialog( HWND hDlg );
  22. HRESULT InitDirectSound( HWND hDlg );
  23. HRESULT FreeDirectSound();
  24. VOID    OnOpenSoundFile( HWND hDlg );
  25. VOID    LoadWaveAndCreateBuffer( HWND hDlg, TCHAR* strFileName );
  26. HRESULT InitDSoundNotification();
  27. HRESULT PlayBuffer( BOOL bLooped );
  28. HRESULT HandleNotification( BOOL bLooped );
  29. VOID    OnTimer( HWND hDlg );
  30. VOID    EnablePlayUI( HWND hDlg, BOOL bEnable );
  31.  
  32.  
  33.  
  34.  
  35. //-----------------------------------------------------------------------------
  36. // Defines, constants, and global variables
  37. //-----------------------------------------------------------------------------
  38. #define NUM_PLAY_NOTIFICATIONS  16
  39.  
  40. #define SAFE_DELETE(p)  { if(p) { delete (p);     (p)=NULL; } }
  41. #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
  42.  
  43. CSoundManager*      g_pSoundManager         = NULL;
  44. CStreamingSound*    g_pStreamingSound       = NULL;
  45. HANDLE              g_hNotificationEvent    = NULL;
  46. DWORD               g_dwNotifyThreadID      = 0;
  47. HANDLE              g_hNotifyThread         = NULL;
  48. HINSTANCE           g_hInst                 = NULL;
  49.  
  50.  
  51.  
  52.  
  53. //-----------------------------------------------------------------------------
  54. // Name: WinMain()
  55. // Desc: Entry point for the application.  Since we use a simple dialog for 
  56. //       user interaction we don't need to pump messages.
  57. //-----------------------------------------------------------------------------
  58. INT APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR pCmdLine, 
  59.                       INT nCmdShow )
  60. {
  61.     g_hInst = hInst;
  62.     g_hNotificationEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  63.  
  64.     // Display the main dialog box.
  65.     DialogBox( hInst, MAKEINTRESOURCE(IDD_MAIN), NULL, MainDlgProc );
  66.  
  67.     CloseHandle( g_hNotificationEvent );
  68.  
  69.     return TRUE;
  70. }
  71.  
  72.  
  73.  
  74.  
  75. //-----------------------------------------------------------------------------
  76. // Name: NotificationProc()
  77. // Desc: Handles dsound notifcation events
  78. //-----------------------------------------------------------------------------
  79. DWORD WINAPI NotificationProc( LPVOID lpParameter )
  80. {
  81.     HRESULT hr;
  82.     HWND    hDlg = (HWND) lpParameter;
  83.     MSG     msg;
  84.     DWORD   dwResult;
  85.     BOOL    bDone = FALSE;
  86.     BOOL    bLooped;
  87.  
  88.     while( !bDone ) 
  89.     { 
  90.         dwResult = MsgWaitForMultipleObjects( 1, &g_hNotificationEvent, 
  91.                                               FALSE, INFINITE, QS_ALLEVENTS );
  92.         switch( dwResult )
  93.         {
  94.             case WAIT_OBJECT_0 + 0:
  95.                 // g_hNotificationEvent is signaled
  96.  
  97.                 // This means that DirectSound just finished playing 
  98.                 // a piece of the buffer, so we need to fill the circular 
  99.                 // buffer with new sound from the wav file
  100.                 bLooped = ( IsDlgButtonChecked( hDlg, IDC_LOOP_CHECK ) == BST_CHECKED );
  101.                 if( FAILED( hr = g_pStreamingSound->HandleWaveStreamNotification( bLooped ) ) )
  102.                 {
  103.                     DXTRACE_ERR_MSGBOX( TEXT("HandleWaveStreamNotification"), hr );
  104.                     MessageBox( hDlg, L"Error handling DirectSound notifications."
  105.                                L"Sample will now exit.", L"DirectSound Sample", 
  106.                                MB_OK | MB_ICONERROR );
  107.                     bDone = TRUE;
  108.                 }
  109.  
  110.                 break;
  111.  
  112.             case WAIT_OBJECT_0 + 1:
  113.                 // Messages are available
  114.                 while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) 
  115.                 { 
  116.                     if( msg.message == WM_QUIT )
  117.                         bDone = TRUE;
  118.                 }
  119.                 break;
  120.         }
  121.     }
  122.  
  123.     return 0;
  124. }
  125.  
  126.  
  127.  
  128.  
  129. //-----------------------------------------------------------------------------
  130. // Name: MainDlgProc()
  131. // Desc: Handles dialog messages
  132. //-----------------------------------------------------------------------------
  133. INT_PTR CALLBACK MainDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
  134. {
  135.     HRESULT hr;
  136.  
  137.     switch (msg) 
  138.     {
  139.     case WM_INITDIALOG:
  140.         OnInitDialog( hDlg );
  141.         break;
  142.  
  143.     case WM_COMMAND:
  144.         switch ( LOWORD(wParam) )
  145.         {
  146.             case IDC_SOUNDFILE:
  147.                 OnOpenSoundFile( hDlg );
  148.                 break;
  149.  
  150.             case IDCANCEL:
  151.                 PostQuitMessage( 0 );
  152.                 EndDialog( hDlg, IDCANCEL );
  153.                 break;
  154.  
  155.             case IDC_PLAY:
  156.                 {
  157.                     BOOL bLooped = ( IsDlgButtonChecked( hDlg, IDC_LOOP_CHECK ) == BST_CHECKED );
  158.  
  159.                     if( FAILED( hr = PlayBuffer( bLooped ) ) )
  160.                     {
  161.                         DXTRACE_ERR_MSGBOX( TEXT("PlayBuffer"), hr );
  162.                         MessageBox( hDlg, L"Error playing DirectSound buffer."
  163.                                     L"Sample will now exit.", L"DirectSound Sample", 
  164.                                     MB_OK | MB_ICONERROR );
  165.                         EndDialog( hDlg, IDABORT );
  166.                     }
  167.  
  168.                     // Update the UI controls to show the sound as playing
  169.                     EnablePlayUI( hDlg, FALSE );
  170.                 }
  171.                 break;
  172.  
  173.             case IDC_STOP:
  174.                 if( g_pStreamingSound )
  175.                 {
  176.                     g_pStreamingSound->Stop();
  177.                     g_pStreamingSound->Reset();
  178.                 }
  179.  
  180.                 EnablePlayUI( hDlg, TRUE );
  181.                 break;
  182.  
  183.             default:
  184.                 return FALSE; // Didn't handle message
  185.         }
  186.         break;
  187.  
  188.     case WM_TIMER:
  189.         OnTimer( hDlg );
  190.         break;
  191.  
  192.     case WM_DESTROY:
  193.         // Cleanup everything
  194.         KillTimer( hDlg, 1 );    
  195.         SAFE_DELETE( g_pStreamingSound );
  196.         SAFE_DELETE( g_pSoundManager );
  197.  
  198.         // Close down notification thread
  199.         PostThreadMessage( g_dwNotifyThreadID, WM_QUIT, 0, 0 );
  200.         WaitForSingleObject( g_hNotifyThread, INFINITE );
  201.         CloseHandle( g_hNotifyThread );
  202.         break; 
  203.  
  204.     default:
  205.         return FALSE; // Didn't handle message
  206.     }
  207.  
  208.     return TRUE; // Handled message
  209. }
  210.  
  211.  
  212.  
  213.  
  214. //-----------------------------------------------------------------------------
  215. // Name: OnInitDialog()
  216. // Desc: Initializes the dialogs (sets up UI controls, etc.)
  217. //-----------------------------------------------------------------------------
  218. VOID OnInitDialog( HWND hDlg )
  219. {
  220.     HRESULT hr;
  221.  
  222.     // Load the icon
  223.     HICON hIcon = LoadIcon( g_hInst, MAKEINTRESOURCE( IDR_MAINFRAME ) );
  224.  
  225.     // Set the icon for this dialog.
  226.     SendMessage( hDlg, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon
  227.     SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon
  228.  
  229.     // Create a static IDirectSound in the CSound class.  
  230.     // Set coop level to DSSCL_PRIORITY, and set primary buffer 
  231.     // format to stereo, 22kHz and 16-bit output.
  232.     g_pSoundManager = new CSoundManager();
  233.     if( NULL == g_pSoundManager )
  234.     {
  235.         DXTRACE_ERR_MSGBOX( TEXT("Initialize"), E_OUTOFMEMORY );
  236.         EndDialog( hDlg, IDABORT );
  237.         return;
  238.     }
  239.  
  240.     if( FAILED( hr = g_pSoundManager->Initialize( hDlg, DSSCL_PRIORITY ) ) )
  241.     {
  242.         DXTRACE_ERR_MSGBOX( TEXT("Initialize"), hr );
  243.         MessageBox( hDlg, L"Error initializing DirectSound.  Sample will now exit.", 
  244.                           L"DirectSound Sample", MB_OK | MB_ICONERROR );
  245.         EndDialog( hDlg, IDABORT );
  246.         return;
  247.     }
  248.     
  249.     if( FAILED( hr = g_pSoundManager->SetPrimaryBufferFormat( 2, 22050, 16 ) ) )
  250.     {
  251.         DXTRACE_ERR_MSGBOX( TEXT("SetPrimaryBufferFormat"), hr );
  252.         MessageBox( hDlg, L"Error initializing DirectSound.  Sample will now exit.", 
  253.                           L"DirectSound Sample", MB_OK | MB_ICONERROR );
  254.         EndDialog( hDlg, IDABORT );
  255.         return;
  256.     }
  257.  
  258.     // Create a thread to handle DSound notifications
  259.     g_hNotifyThread = CreateThread( NULL, 0, NotificationProc, 
  260.                                     hDlg, 0, &g_dwNotifyThreadID );
  261.  
  262.     // Create a timer, so we can check for when the soundbuffer is stopped
  263.     SetTimer( hDlg, 0, 250, NULL );
  264.  
  265.     // Set the UI controls
  266.     SetDlgItemText( hDlg, IDC_FILENAME, TEXT("No file loaded.") );
  267. }
  268.  
  269.  
  270.  
  271.  
  272. //-----------------------------------------------------------------------------
  273. // Name: OnOpenSoundFile()
  274. // Desc: Called when the user requests to open a sound file
  275. //-----------------------------------------------------------------------------
  276. VOID OnOpenSoundFile( HWND hDlg ) 
  277. {
  278.     static TCHAR strFileName[MAX_PATH] = TEXT("");
  279.     static TCHAR strPath[MAX_PATH] = TEXT("");
  280.  
  281.     // Setup the OPENFILENAME structure
  282.     OPENFILENAME ofn = { sizeof(OPENFILENAME), hDlg, NULL,
  283.                          TEXT("Wave Files\0*.wav\0All Files\0*.*\0\0"), NULL,
  284.                          0, 1, strFileName, MAX_PATH, NULL, 0, strPath,
  285.                          TEXT("Open Sound File"),
  286.                          OFN_FILEMUSTEXIST|OFN_HIDEREADONLY, 0, 0,
  287.                          TEXT(".wav"), 0, NULL, NULL };
  288.  
  289.     // Get the default media path (something like C:\WINDOWS\MEDIA)
  290.     if( '\0' == strPath[0] )
  291.     {
  292.         if( GetWindowsDirectory( strPath, MAX_PATH ) != 0 )
  293.         {
  294.             if( wcscmp( &strPath[wcslen(strPath)], TEXT("\\") ) )
  295.                 wcscat( strPath, TEXT("\\") );
  296.             wcscat( strPath, TEXT("MEDIA") );
  297.         }
  298.     }
  299.  
  300.     if( g_pStreamingSound )
  301.     {
  302.         g_pStreamingSound->Stop();
  303.         g_pStreamingSound->Reset();
  304.     }
  305.  
  306.     // Update the UI controls to show the sound as loading a file
  307.     EnableWindow( GetDlgItem( hDlg, IDC_LOOP_CHECK ), FALSE );
  308.     EnableWindow( GetDlgItem( hDlg, IDC_PLAY ),       FALSE );
  309.     EnableWindow( GetDlgItem( hDlg, IDC_STOP ),       FALSE );
  310.     SetDlgItemText( hDlg, IDC_FILENAME, TEXT("Loading file...") );
  311.  
  312.     // Display the OpenFileName dialog. Then, try to load the specified file
  313.     if( TRUE != GetOpenFileName( &ofn ) )
  314.     {
  315.         SetDlgItemText( hDlg, IDC_FILENAME, TEXT("Load aborted.") );
  316.         return;
  317.     }
  318.  
  319.     SetDlgItemText( hDlg, IDC_FILENAME, TEXT("") );
  320.  
  321.     // Load the wave file and create a DirectSound buffer
  322.     LoadWaveAndCreateBuffer( hDlg, strFileName );
  323.  
  324.     // Remember the path for next time
  325.     wcscpy( strPath, strFileName );
  326.     WCHAR* strLastSlash = wcsrchr( strPath, '\\' );
  327.     if( strLastSlash )
  328.         strLastSlash[0] = '\0';
  329. }
  330.  
  331.  
  332.  
  333.  
  334.  
  335. //-----------------------------------------------------------------------------
  336. // Name: LoadWaveAndCreateBuffer()
  337. // Desc: Loads the wave file, and create a DirectSound buffer.  Since we are
  338. //       streaming data into the buffer, the buffer will be filled with data 
  339. //       when the sound is played, and as notification events are signaled
  340. //-----------------------------------------------------------------------------
  341. VOID LoadWaveAndCreateBuffer( HWND hDlg, TCHAR* strFileName )
  342. {
  343.     HRESULT   hr;
  344.     CWaveFile waveFile;
  345.     DWORD     dwNotifySize;
  346.  
  347.     // Load the wave file
  348.     if( FAILED( hr = waveFile.Open( strFileName, NULL, WAVEFILE_READ ) ) )
  349.     {
  350.         DXTRACE_ERR_MSGBOX( TEXT("Open"), hr );
  351.         waveFile.Close();
  352.         SetDlgItemText( hDlg, IDC_FILENAME, TEXT("Bad wave file.") );
  353.         return;
  354.     }
  355.  
  356.     if( waveFile.GetSize() == 0 )
  357.     {
  358.         waveFile.Close();
  359.         SetDlgItemText( hDlg, IDC_FILENAME, TEXT("Wave file blank.") );
  360.         return;
  361.     }
  362.  
  363.     // The wave file is valid, and waveFile.m_pwfx is the wave's format
  364.     // so we are done with the reader.
  365.     waveFile.Close();
  366.  
  367.     // This samples works by dividing a 3 second streaming buffer into 
  368.     // NUM_PLAY_NOTIFICATIONS (16) pieces.  It creates a notification for each
  369.     // piece and when a notification arrives then it fills the circular streaming 
  370.     // buffer with new wav data over the sound data which was just played
  371.  
  372.     // Determine the g_dwNotifySize.  It should be an integer multiple of nBlockAlign
  373.     DWORD nBlockAlign = (DWORD)waveFile.m_pwfx->nBlockAlign;
  374.     INT nSamplesPerSec = waveFile.m_pwfx->nSamplesPerSec;
  375.     dwNotifySize = nSamplesPerSec * 3 * nBlockAlign / NUM_PLAY_NOTIFICATIONS;
  376.     dwNotifySize -= dwNotifySize % nBlockAlign;   
  377.  
  378.     // Create a new sound
  379.     SAFE_DELETE( g_pStreamingSound );
  380.  
  381.     // Set up the direct sound buffer.  Request the NOTIFY flag, so
  382.     // that we are notified as the sound buffer plays.  Note, that using this flag
  383.     // may limit the amount of hardware acceleration that can occur. 
  384.     if( FAILED( hr = g_pSoundManager->CreateStreaming( &g_pStreamingSound, strFileName, 
  385.                                                   DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2, 
  386.                                                   GUID_NULL, NUM_PLAY_NOTIFICATIONS, 
  387.                                                   dwNotifySize, g_hNotificationEvent ) ) )
  388.     {
  389.         if( hr != DSERR_BADFORMAT && hr != E_INVALIDARG )
  390.             DXTRACE_ERR_MSGBOX( TEXT("CreateStreaming"), hr );
  391.  
  392.         SetDlgItemText( hDlg, IDC_FILENAME, TEXT("Could not support the file's audio format.") );
  393.         return;
  394.     }
  395.  
  396.     // Update the UI controls to show the sound as the file is loaded
  397.     EnablePlayUI( hDlg, TRUE );
  398.     SetDlgItemText( hDlg, IDC_FILENAME, strFileName );
  399. }
  400.  
  401.  
  402.  
  403.  
  404. //-----------------------------------------------------------------------------
  405. // Name: PlayBuffer()
  406. // Desc: Reset the buffer, fill it with sound, and starting it playing
  407. //-----------------------------------------------------------------------------
  408. HRESULT PlayBuffer( BOOL bLooped )
  409. {
  410.     HRESULT hr;
  411.     
  412.     if( NULL == g_pStreamingSound )
  413.         return E_FAIL; // Sanity check
  414.  
  415.     if( FAILED( hr = g_pStreamingSound->Reset() ) )
  416.         return DXTRACE_ERR_MSGBOX( TEXT("Reset"), hr );
  417.  
  418.     // Fill the entire buffer with wave data, and if the wav file is small then
  419.     // repeat the wav file if the user wants to loop the file, otherwise fill in
  420.     // silence 
  421.     LPDIRECTSOUNDBUFFER pDSB = g_pStreamingSound->GetBuffer( 0 );
  422.     if( FAILED( hr = g_pStreamingSound->FillBufferWithSound( pDSB, bLooped ) ) )
  423.         return DXTRACE_ERR_MSGBOX( TEXT("FillBufferWithSound"), hr );
  424.  
  425.     // Always play with the LOOPING flag since the streaming buffer
  426.     // wraps around before the entire WAV is played
  427.     if( FAILED( hr = g_pStreamingSound->Play( 0, DSBPLAY_LOOPING ) ) )
  428.         return DXTRACE_ERR( TEXT("Play"), hr );
  429.  
  430.     return S_OK;
  431. }
  432.  
  433.  
  434.  
  435.  
  436. //-----------------------------------------------------------------------------
  437. // Name: OnTimer()
  438. // Desc: When we think the sound is playing this periodically checks to see if 
  439. //       the sound has stopped.  If it has then updates the dialog.
  440. //-----------------------------------------------------------------------------
  441. VOID OnTimer( HWND hDlg ) 
  442. {
  443.     if( IsWindowEnabled( GetDlgItem( hDlg, IDC_STOP ) ) )
  444.     {
  445.         // We think the sound is playing, so see if it has stopped yet.
  446.         if( !g_pStreamingSound->IsSoundPlaying() ) 
  447.         {
  448.             // Update the UI controls to show the sound as stopped
  449.             EnablePlayUI( hDlg, TRUE );
  450.         }
  451.     }
  452. }
  453.  
  454.  
  455.  
  456.  
  457. //-----------------------------------------------------------------------------
  458. // Name: EnablePlayUI( hDlg,)
  459. // Desc: Enables or disables the Play UI controls 
  460. //-----------------------------------------------------------------------------
  461. VOID EnablePlayUI( HWND hDlg, BOOL bEnable )
  462. {
  463.     if( bEnable )
  464.     {
  465.         EnableWindow( GetDlgItem( hDlg, IDC_LOOP_CHECK ), TRUE );
  466.         EnableWindow( GetDlgItem( hDlg, IDC_PLAY ),       TRUE );
  467.         EnableWindow( GetDlgItem( hDlg, IDC_STOP ),       FALSE );
  468.         SetFocus(     GetDlgItem( hDlg, IDC_PLAY ) );
  469.     }
  470.     else
  471.     {
  472.         EnableWindow( GetDlgItem( hDlg, IDC_LOOP_CHECK ), FALSE );
  473.         EnableWindow( GetDlgItem( hDlg, IDC_PLAY ),       FALSE );
  474.         EnableWindow( GetDlgItem( hDlg, IDC_STOP ),       TRUE );
  475.         SetFocus(     GetDlgItem( hDlg, IDC_STOP ) );
  476.     }
  477. }
  478.  
  479.  
  480.  
  481.  
  482.